package org.openintents.oiserverdemo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.openintents.webserver.WebServerWrapper;

import android.app.Service;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;

public class OIServerService extends Service {

	/* Constants */
	public static final int STATUS_STARTED = 1;
	public static final int STATUS_STOPPED = 2;
	public static final int STATUS_ERROR = 3;
	
	public static final String ACTION_MODIFIED = "org.openintents.action.MODIFIED";
	
	private WebServerWrapper serverWrapper;
	private File wwwRoot;
	private int port;
	private Cursor notesCursor;
	private HTMLFileGenerator generator;
	public static boolean isRunning = false;
	public static final String STATUS_CHANGED_INTENT = "org.openintents.oiserverdemo.intent.action.STATUS_CHANGED";
	private VelocityEngine engine;
	
	private Uri notesURI = Uri.parse("content://org.openintents.notepad/notes");
	
	private class NotepadContentObserver extends ContentObserver {

		public NotepadContentObserver() {
			super(null);
		}

		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
			
			//Log.d(getPackageName(), "**** CHANGED ****");
			notesCursor.close();
			notesCursor = getNewNotesCursor();
			if(!notesCursor.isFirst())
				notesCursor.moveToFirst();
			
			try {
				//notesCursor = getNewNotesCursor();
				//notesCursor.moveToFirst();
				//Log.d(getPackageName(), "**** TITLE: "+notesCursor.getString(notesCursor.getColumnIndex("TITLE")));
				//notesCursor.moveToFirst();				
				//generator.generateNotepadFile(new File(wwwRoot.getAbsolutePath()+"/oinotepad.html"), notesCursor);
				refreshDataModel();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	private NotepadContentObserver notepadContentObserver = new NotepadContentObserver();
	private boolean enableSSL;
	private WebServerWrapper sslServerWrapper;
	private String host;
	
	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}

	/* (non-Javadoc)
	 * @see android.app.Service#onCreate()
	 */
	@Override
	public void onCreate() {
		super.onCreate();
		/*try {
			saveKeyStoreFile();
		} catch (IOException e) {
			e.printStackTrace();
			changeStatus(STATUS_ERROR, e.getMessage());
		}*/
		engine = new VelocityEngine();
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		serverWrapper.stop();
		sslServerWrapper.stop();
		isRunning = false;
		changeStatus(STATUS_STOPPED);
		getContentResolver().unregisterContentObserver(notepadContentObserver);
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		
		if(isRunning) {
			stopSelf(startId);
			return 0;
		}
		
		try {
			
			wwwRoot = new File(intent.getStringExtra("wwwroot"));
			port = intent.getIntExtra("port", 8080);
			enableSSL = intent.getBooleanExtra("enableSSL", false);
			host = intent.getStringExtra("host");
			
			notesCursor = getNewNotesCursor();
        	
			engine.setProperty("resource.loader", "file");
			engine.setProperty("class.resource.loader.class", 
					"org.apache.velocity.runtime.resource.loader.FileResourceLoader");
			engine.setProperty("file.resource.loader.path",
					wwwRoot.getAbsolutePath() + "/templates");
			engine.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS,
					"org.openintents.oiserverdemo.VelocityLogger");
			
			engine.init();
						
			getContentResolver().registerContentObserver(notesURI, true, notepadContentObserver);
	        
			refreshDataModel();
			
			if(enableSSL) {
				sslServerWrapper = new WebServerWrapper(8888, wwwRoot, true, getAssets().open("oiserverdemo.bks"));
				
				serverWrapper = new WebServerWrapper(port, wwwRoot, true, null);
				serverWrapper.setSSLPort(8888);
				serverWrapper.setHostname(host);
			}
			else
				serverWrapper = new WebServerWrapper(port, wwwRoot, false, null);
			
			isRunning = true;
			//showMessage("Started", "Server started\nYou can connect to this device using the following address: "+
			//		"http://"+OIServerDemoActivity.getIPAddress()+":2222");
			changeStatus(STATUS_STARTED);
			//Toast.makeText(getApplicationContext(), "Server started", Toast.LENGTH_LONG);
			//registerReceiver(broadcastReciever, new IntentFilter(ACTION_MODIFIED));
			
		} catch (IOException e) {
			e.printStackTrace();
			//showMessage("Error", e.getMessage());
			changeStatus(STATUS_ERROR, "Exception: "+e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			changeStatus(STATUS_ERROR, e.getMessage());
		}
		
		return START_STICKY;
	}
	
	private Cursor getNewNotesCursor() {
		//notesCursor.close();
		return getContentResolver().query(notesURI, null, null, null, null);
	}
	
    private void changeStatus(int id) {
    	Intent intent = new Intent();
    	intent.setAction(STATUS_CHANGED_INTENT);
    	intent.putExtra("status", id);
    	sendBroadcast(intent);
    }
    
    private void changeStatus(int id, String msg) {
    	Intent intent = new Intent();
    	intent.setAction(STATUS_CHANGED_INTENT);
    	intent.putExtra("status", id);
    	intent.putExtra("msg", msg);
    	sendBroadcast(intent);
    }
    
    private void refreshDataModel() throws IOException {
    	Writer writer = new OutputStreamWriter(
    			new FileOutputStream(wwwRoot.getAbsolutePath()+"/index.html"));
    	
    	Log.d(getPackageName(), (String)Velocity.getProperty("file.resource.loader.path"));
    	
    	VelocityContext indexContext = new VelocityContext();
    	indexContext.put("title", "OIServerDemo");
    	indexContext.put("introText", getResources().getString(R.string.introText));
    	
    	Template indexTemplate = engine.getTemplate("index.vm");
    	
    	indexTemplate.merge(indexContext, writer);
    	writer.close();
    	
    	refreshNotepadModel();
    }
    
    private void refreshNotepadModel() throws IOException {
    	
    	VelocityContext notepadContext = new VelocityContext();
    	
    	notepadContext.put("notepadIntroText", getResources().getString(R.string.notepadIntroText));
    	notepadContext.put("title", "OIServerDemo - OI Notepad");
    	
    	Map<String, Object> notepadMap = new HashMap<String, Object>();
    	notepadMap.put("notepadIntroText", getResources().getString(R.string.notepadIntroText));
    	
    	List<Map<Object, Object>> noteList = new ArrayList<Map<Object,Object>>();
    	
    	notesCursor.moveToFirst();
    	
    	do {
    		String title = notesCursor.getString(notesCursor.getColumnIndex("TITLE"));
    		String text = notesCursor.getString(notesCursor.getColumnIndex("NOTE"));
    		int id = notesCursor.getInt(notesCursor.getColumnIndex("_ID"));
    		
	    	Map<Object, Object> note = new HashMap<Object, Object>();
	    	
	    	note.put("id", Integer.toString(id));
	    	note.put("title", title);
	    	note.put("text", text);
	    	
	    	noteList.add(note);
    	} while(notesCursor.moveToNext());
    	
    	notepadContext.put("noteList", noteList);
    	
    	Template notepadTemplate = engine.getTemplate("oinotepad.vm");
    	
    	OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(wwwRoot.getAbsolutePath()+"/oinotepad.html"));
    	
    	notepadTemplate.merge(notepadContext, writer);
    	writer.close();
    }
    
    private void saveKeyStoreFile() throws IOException {
    	InputStream istr = getAssets().open("oiserverdemo.bks");
		if(istr == null) {
			System.out.println("Error: Cannot get keystore file!");
			changeStatus(STATUS_ERROR, "Cannot load keystore!");
			return;
		}
		File certFile = new File(getCacheDir().getAbsolutePath()+"/oiserverdemo.bks");
		OutputStream ostr = new FileOutputStream(certFile);
		byte[] buffer = new byte[4096];
		int len;
		while((len = istr.read(buffer)) != -1) {
			ostr.write(buffer, 0, len);
		}
		ostr.close();
		istr.close();
		/*System.setProperty("javax.net.ssl.keyStore", certFile.getAbsolutePath());
		System.setProperty("javax.net.ssl.keyStorePassword", "openintents");
		//TODO: Is this necessary?
		System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
		System.setProperty("javax.net.debug", "ssl");
		Toast.makeText(getApplicationContext(), certFile.getAbsolutePath(), Toast.LENGTH_LONG).show();*/
    }
    
}
